<?php

Raise::load('core.RaiseDataType');
Raise::load('core.helper.sorting.IRaiseComparable');
Raise::load('RaiseTimeComparator');

class RaiseTime extends RaiseDataType implements IRaiseComparable {
    
    /**
     * Hour of the day (0 to 23), 24-hour format
     * @var integer
     */
    private $hour = 0;
    
    /**
     * Minutes of the hour (0 to 59)
     * @var integer
     */
    private $minute = 0;
    
    /**
     * Seconds of the minute (0 to 59)
     * @var integer
     */
    private $second = 0;
    
    /**
     * milliseconds
     * @var integer
     */
    private $millisecond = 0;
    
    /**
     * Create a new RaiseTime object
     * @param integer $hour (optional) The hour component. Defaults to 0.
     * @param integer $minute (optional) The minute component. Defaults to 0.
     * @param integer $second (optional) The second component. Defaults to 0.
     * @param double|integer $millisecond (optional) The millisecond component. Defaults to 0.
     */
    function __construct($hour = 0, $minute = 0, $second = 0, $millisecond = 0) {
        $this->hour($hour);
        $this->minute($minute);
        $this->second($second);
        $this->millisecond($millisecond);
    }
    
    private function processNextComponent($value, $next, $max){
        if($value >= $max){
            $addNext = (int)floor($value / $max);
            $this->$next($this->$next + $addNext);
            $value -= $addNext * $max;
        }elseif($value < 0){
            $subNext = (int)floor(abs($value) / $max);
            $this->$next($this->$next - $subNext - 1);
            $value = $max + ($value + ($subNext * $max));
        }
        return $value;
    }

    /**
     * The millisecond component of the RaiseDateTime
     * @param integer $a An integer that represents millisecond (1000th of a second)
     * @return integer
     */
    public function millisecond($a = null){
        if(func_num_args() == 1){
            if($a != $this->millisecond){
                $a = $this->processNextComponent($a, 'second', 1000);
                $this->millisecond = (int)$a;
            }
        }
        return $this->millisecond;
    }

    /**
     * The seconds component of the RaiseDateTime
     * @param integer $a An integer from 0 to 59 that represents the 60 seconds in 1 minute
     * @return integer
     */
    public function second($a = null){
        if(func_num_args() == 1){
            if($a != $this->second){
                $a = $this->processNextComponent($a, 'minute', 60);
                $this->second = (int)$a;
            }
        }
        return $this->second;
    }

    /**
     * The minute component of the RaiseDateTime
     * @param integer $a An integer from 0 to 59 that represents the 60 minutes in 1 hour
     * @return integer
     */
    public function minute($a = null){
        if(func_num_args() == 1){
            if($a != $this->minute){
                $a = $this->processNextComponent($a, 'hour', 60);
                $this->minute = (int)$a;
            }
        }
        return $this->minute;
    }

    /**
     * The hour component of the RaiseDateTime
     * @param integer $value An integer from 0 to 23 that represents 24 hours
     * @return integer
     */
    public function hour($value = null){
        if(func_num_args() == 1){
            if($value != $this->hour){
                if($value >= 24){
                    $addNext = (int)floor($value / 24);
                    $value -= $addNext * 24;
                }
                if($value < 0){
                    $subNext = (int)floor(abs($value) / 24);
                    $value = 24 + ($value + $subNext * 24);
                }
                $this->hour = (int)$value;
            }
        }
        return $this->hour;
    }
    
    /**
     * Get the total number of seconds from midnight 00:00:00.0
     * @return double|integer
     */
    public function totalSeconds(){
        return $this->hour * 3600 + $this->minute * 60 + $this->second + ($this->millisecond / 1000);
    }
    
    /**
     * Add another time to this time. If the sum of the two time object passes 24 hours (i.e. a day), the result will be the time next day.
     * @param RaiseTime $time The amount of time to add.
     * @return RaiseTime
     */
    public function add($time){
        $temp = $this->raiseClone();
        
        $temp->millisecond($temp->millisecond + $time->millisecond);
        $temp->second($temp->second + $time->second);
        $temp->minute($temp->minute + $time->minute);
        $temp->hour($temp->hour + $time->hour);
        
        return $temp;
    }

    /**
     * Subtract some time from this time. If the result is negative, it will return the time of the previous day.
     * @param RaiseTime $time The amount of time to deduct
     * @return RaiseTime The resulting RaiseTimeSpan from the subtract operation
     */
    public function subtract($time){
        $temp = $this->raiseClone();
        
        $temp->millisecond($temp->millisecond - $time->millisecond);
        $temp->second($temp->second - $time->second);
        $temp->minute($temp->minute - $time->minute);
        $temp->hour($temp->hour - $time->hour);
        
        return $temp;
    }

    /**
     * Compare this time with another RaiseTime object
     * @param RaiseTime $time 
     */
    public function compareTo($time) {
        $comparator = new RaiseTimeComparator();
        return $comparator->compare($this, $time);
    }
    
}